﻿//  ---------------------------------------------------------------------------------
//  Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  The MIT License (MIT)
// 
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//  ---------------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;

namespace ScreenCapture
{
    internal static class WindowEnumerationHelper
    {
        public enum GWL
        {
            GWL_WNDPROC    = -4,
            GWL_HINSTANCE  = -6,
            GWL_HWNDPARENT = -8,
            GWL_STYLE      = -16,
            GWL_EXSTYLE    = -20,
            GWL_USERDATA   = -21,
            GWL_ID         = -12
        }

        [DllImport( "user32.dll" )]
        private static extern IntPtr GetShellWindow();

        [DllImport( "user32.dll" )]
        [return: MarshalAs( UnmanagedType.Bool )]
        private static extern bool IsWindowVisible( IntPtr hWnd );

        [DllImport( "user32.dll", ExactSpelling = true )]
        private static extern IntPtr GetAncestor( IntPtr hwnd, GetAncestorFlags flags );

        [DllImport( "user32.dll", EntryPoint = "GetWindowLong" )]
        private static extern IntPtr GetWindowLongPtr32( IntPtr hWnd, int nIndex );

        [DllImport( "user32.dll", EntryPoint = "GetWindowLongPtr" )]
        private static extern IntPtr GetWindowLongPtr64( IntPtr hWnd, int nIndex );

        // This static method is required because Win32 does not support
        // GetWindowLongPtr directly.
        // http://pinvoke.net/default.aspx/user32/GetWindowLong.html
        private static IntPtr GetWindowLongPtr( IntPtr hWnd, int nIndex )
        {
            if ( IntPtr.Size == 8 )
                return GetWindowLongPtr64( hWnd, nIndex );
            else
                return GetWindowLongPtr32( hWnd, nIndex );
        }

        [DllImport( "dwmapi.dll" )]
        private static extern int DwmGetWindowAttribute( IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute, out bool pvAttribute, int cbAttribute );

        public static bool IsWindowValidForCapture( IntPtr hwnd )
        {
            if ( hwnd.ToInt32() == 0 )
            {
                return false;
            }

            if ( hwnd == GetShellWindow() )
            {
                return false;
            }

            if ( !IsWindowVisible( hwnd ) )
            {
                return false;
            }

            if ( GetAncestor( hwnd, GetAncestorFlags.GetRoot ) != hwnd )
            {
                return false;
            }

            var style = (WindowStyles)(uint)GetWindowLongPtr( hwnd, (int)GWL.GWL_STYLE ).ToInt32();
            if ( style.HasFlag( WindowStyles.WS_DISABLED ) )
            {
                return false;
            }

            var cloaked = false;
            var hrTemp  = DwmGetWindowAttribute( hwnd, DWMWINDOWATTRIBUTE.Cloaked, out cloaked, Marshal.SizeOf<bool>() );
            if ( hrTemp == 0 && cloaked )
            {
                return false;
            }

            return true;
        }

        private enum GetAncestorFlags
        {
            // Retrieves the parent window. This does not include the owner, as it does with the GetParent function.
            GetParent = 1,

            // Retrieves the root window by walking the chain of parent windows.
            GetRoot = 2,

            // Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent.
            GetRootOwner = 3
        }

        [Flags]
        private enum WindowStyles : uint
        {
            WS_BORDER           = 0x800000,
            WS_CAPTION          = 0xc00000,
            WS_CHILD            = 0x40000000,
            WS_CLIPCHILDREN     = 0x2000000,
            WS_CLIPSIBLINGS     = 0x4000000,
            WS_DISABLED         = 0x8000000,
            WS_DLGFRAME         = 0x400000,
            WS_GROUP            = 0x20000,
            WS_HSCROLL          = 0x100000,
            WS_MAXIMIZE         = 0x1000000,
            WS_MAXIMIZEBOX      = 0x10000,
            WS_MINIMIZE         = 0x20000000,
            WS_MINIMIZEBOX      = 0x20000,
            WS_OVERLAPPED       = 0x0,
            WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
            WS_POPUP            = 0x80000000u,
            WS_POPUPWINDOW      = WS_POPUP | WS_BORDER | WS_SYSMENU,
            WS_SIZEFRAME        = 0x40000,
            WS_SYSMENU          = 0x80000,
            WS_TABSTOP          = 0x10000,
            WS_VISIBLE          = 0x10000000,
            WS_VSCROLL          = 0x200000
        }

        private enum DWMWINDOWATTRIBUTE : uint
        {
            NCRenderingEnabled = 1,
            NCRenderingPolicy,
            TransitionsForceDisabled,
            AllowNCPaint,
            CaptionButtonBounds,
            NonClientRtlLayout,
            ForceIconicRepresentation,
            Flip3DPolicy,
            ExtendedFrameBounds,
            HasIconicBitmap,
            DisallowPeek,
            ExcludedFromPeek,
            Cloak,
            Cloaked,
            FreezeRepresentation
        }
    }
}